﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OmegaEngine
{
    public class GameScreen
    {
        // Keep track of all the components we need to manage
        public ComponentCollection Components;

        // Keep track of all GUI components
        public GUIManager GUIManager;

        // Whether or not to draw
        public bool Visible = true;

        // Whether or not this screen should block the update of screens below (IE pause menu)
        public bool BlocksUpdate = false;

        // Whether or not this screen can override a blocked update from an above screen (for a background screen), etc
        public bool OverrideUpdateBlocked = false;

        // Whether or not this screen should block the drawing of screens below it
        public bool BlocksDraw = false;

        // Whether or not this screen can override a blocked draw from an above screen
        public bool OverrideDrawBlocked = false;

        // Whether or not this screen should block input for screens below it
        public bool BlocksInput = false;

        // Whether or not this screen can override a block input from an above screen
        public bool OverrideInputBlocked = false;

        // Whether or not we want to block our own input so we can do things like loading screens that will
        // want to accept input at some point, but not at startup
        public bool InputDisabled = false;

        // This is set by the engine to tell us whether or not input is allow. We can still get input, but we shouldn't.
        // This is useful because a ProcessInput() type of function would make it hard to manage input (becase we can't utilize events, etc)
        public bool IsInputAllowed = true;

        // The name of our component, set in the constructor. This is use by the Engine because a GameScreen can be accessed
        // by name from Engine.Gamescreens[Name].
        public string Name;

        // Fired when the component's Initialize() is finished. This can be hooked for things like asynchronous loading screens
        public event EventHandler OnInitialized;

        // Whether or not he component is initialized. Handles firing of OnInialized.
        bool initialized = false;

        public bool Initialized
        {
            get { return initialized; }
            set
            {
                initialized = value;

                if(OnInitialized != null)
                {
                    // Fire the OnInitialized event to let other's know we are done initializing
                    OnInitialized(this, new EventArgs());
                }
            }
        }

        // Constructor take the name of the component
        public GameScreen(String Name)
        {
            // Setup our component collection
            Components = new ComponentCollection(this);

            // Register with the engine and set our name
            this.Name = Name;
            Engine.GameScreens.Add(this);

            // Initialize the 2D manager
            this.GUIManager = new GUIManager(this);
            this.AddComponent(this.GUIManager);

            // Initialize the component
            if(!Initialized)
                Initialize();
        }
        
        // Function to add components to the game screen
        public void AddComponent(Component Component)
        {
            if (!Components.Contains(Component))
            {
                if (Component is I2DComponent && !(Component is GUIManager))
                    this.GUIManager.AddComponent(Component);
                else
                    Components.Add(Component);

                Component.Parent = this;
            }
        }

        // Allows for components to be retried with a [] index, ie:
        // Component c = gameScreenInstance['Component1'];
        public Component this[String Name]
        {
            get
            {
                foreach (Component component in Components)
                    if (component.Name == Name)
                        return component;

                return null;
            }
        }

        // Remove component from GameScreen by name
        public void RemoveComponent(string Name)
        {
            Component c = this[Name];
            RemoveComponent(c);
        }

        // Remove component from GameScreen by match component
        public void RemoveComponent(Component Component)
        {
            if (Component != null && Components.Contains(Component))
            {
                Components.Remove(Component);
                Component.Parent = null;
            }
        }

        // Overridable function to initialize the GameScreen
        public virtual void Initialize()
        {
            this.Initialized = true;
        }

        // Update the screen and child COmponents
        public virtual void Update()
        {
            // Create a temporary list so that we don't crash if a component is added to the collection while updating
            List <Component> updating = new List<Component>();

            // Populate the temporary list
            foreach (Component c in Components)
                updating.Add(c);

            // Update all components that have been initialized
            foreach (Component Component in updating)
                if(Component.Initialized)
                    Component.Update();
        }

        // Draw the screen and its components. Accepts a ComponentsType to tell us what kind of components to draw. Either
        // 2D, 3D, or both. (Useful for drawing a reflection into a render target without 2D components getting in the way)
        public virtual void Draw(ComponentType RenderType)
        {
            // Temporary list
            List<Component> drawing = new List<Component>();

            foreach (Component component in Components.InDrawOrder)
            {
                if(RenderType != ComponentType.Component2D)
                    if(component is I3DComponent)
                        drawing.Add(component);

                // This was useful before the GUI manager.
                /*
                if(RenderType == ComponentType.Both)
                {
                    // If the render type is both, we will draw all 2D or 3D components
                    if(component is I2DComponent || component is I3DComponent)
                        drawing.Add(component);
                }
                else if (RenderType == ComponentType.Component2D)
                {
                    // If the render type is in 2D, we will only drawn 2D components
                    if(component is I2DComponent)
                        drawing.Add(component);
                }
                else if (RenderType == ComponentType.Component3D)
                {
                    // If the render type is in 3D, we will only drawn 3D components
                    if(component is I3DComponent)
                        drawing.Add(component);
                }
                else
                {
                    // Otherwise, we will drawn every component regardless of type
                    drawing.Add(component);
                }
                */
            }

            // Keep a list of components that are 2D so we can drawn them on top of the 3D components
            //List<Component> defer2D = new List<Component>();

            foreach (Component component in drawing)
                if(component.Visible && component.Initialized)
                {
                    // If the component is visible and is not loading itself...
                    //if(component is I2DComponent)
                    //{
                    //    // If it is 2D, wait to draw
                    //    defer2D.Add(component);
                    //}
                    //else
                    //{
                    //    // otherwise, draw immediately
                        component.Draw();
                    //}
                }

            // Draw 2D components
            GUIManager.Draw();
            //foreach(Component component in defer2D)
            //    component.Draw();
        }

        // Disables the GameScreen
        public virtual void Disable()
        {
            // Clear out our components
            Components.Clear();

            // Unregister from the Engine's list
            Engine.GameScreens.Remove(this);

            // If the engine happens to have this screen set as the default screen, set it to the background screen in the Engine class
            if(Engine.DefaultScreen == this)
                Engine.DefaultScreen = Engine.BackgroundScreen;
        }

        // Override ToString() to return our name
        public override string ToString()
        {
               return Name;
        }
    }
}